package com.siyoumi.util;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.LinkedList;
import java.util.List;

//时间工具类
@Slf4j
public class XDate {
    /**
     * 时间格式(yyyy-MM-dd 00:00:00)
     */
    public final static DateTimeFormatter DATE_PATTERN_00 = DateTimeFormatter.ofPattern("yyyy-M-d 00:00:00");

    public final static DateTimeFormatter yyyyMMdd_PATTERN = DateTimeFormatter.ofPattern("yyyyMMdd");

    /**
     * 时间格式(yyyy-MM-dd)
     */
    public final static DateTimeFormatter DATE_PATTERN = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    /**
     * 时间格式(yyyy-MM-dd HH:mm:ss)
     */
    public final static DateTimeFormatter DATE_TIME_PATTERN = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    public final static DateTimeFormatter DATE_TIME_PATTERN_Si = DateTimeFormatter.ofPattern("yyyy-M-d H:m:s");
    public final static DateTimeFormatter DATE_TIME_SHORT_PATTERN = DateTimeFormatter.ofPattern("MM-dd HH:mm:ss");

    public final static DateTimeFormatter DATE_TIME_ISO_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX");


    public static String toDateString(LocalDateTime date) {
        if (date == null) {
            date = LocalDateTime.now();
        }

        return format(date, DATE_PATTERN);
    }

    public static String toDateString() {
        return toDateString(null);
    }

    public static String toDateTimeString(LocalDateTime date) {
        if (date == null) {
            date = LocalDateTime.now();
        }

        return format(date, DATE_TIME_PATTERN);
    }

    public static String toDateTimeString() {
        return toDateTimeString(null);
    }

    public static LocalDateTime now() {
        return LocalDateTime.now();
    }

    public static LocalDateTime today() {
        return parse(toDateString(now()));
    }

    /**
     * 今天，转数字
     */
    public static Integer toNum(LocalDateTime date) {
        String yyyyMMdd = format(date, "yyyyMMdd");
        return XStr.toInt(yyyyMMdd);
    }

    /**
     * 本月第1天
     */
    public static LocalDateTime month01(LocalDateTime date) {
        return parse(format(date, "yyyy-MM-01"));
    }

    public static LocalDateTime toDate(LocalDateTime date) {
        return LocalDateTime.of(date.getYear(), date.getMonthValue(), date.getDayOfMonth(), 0, 0);
    }

    public static LocalDateTime date2000() {
        return XStr.toDateTime("2000-01-01 00:00:00");
    }

    /**
     * 日期格式化 日期格式为：yyyy-MM-dd
     *
     * @param date    日期
     * @param pattern 格式，如：DateUtils.DATE_TIME_PATTERN
     * @return 返回yyyy-MM-dd格式日期
     */
    static public String format(LocalDateTime date, DateTimeFormatter pattern) {
        if (date != null) {
            return date.format(pattern);
        }

        return null;
    }

    static public String format(LocalDateTime date, String pattern) {
        return format(date, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 获取求两个日期相差对象
     *
     * @param b 开始时间
     * @param e 结束时间
     * @return 处理对象
     */
    public static Duration between(LocalDateTime b, LocalDateTime e) {
        return Duration.between(b, e);
    }

    /**
     * 时间差（秒）
     *
     * @param b
     * @param e
     */
    static public Long leftSeconds(LocalDateTime b, LocalDateTime e) {
        long seconds = between(b, e).getSeconds();
        if (seconds <= 0) {
            seconds = 0;
        }
        return seconds;
    }

    /**
     * 返回以毫秒为单位的当前时间
     *
     * @return 当前时间(毫秒)
     */
    static public Long toMs() {
        return toMs(XDate.now());
    }

    static public Long toMs(LocalDateTime dt) {
        return dt.toInstant(ZoneOffset.of("+8")).toEpochMilli();
    }

    static public Long toS(LocalDateTime dt) {
        return toMs(dt) / 1000;
    }

    static public Long toS() {
        return toMs() / 1000;
    }

    /**
     * @param s 时间戳（秒）
     */
    static public LocalDateTime parseTimestamp(Long s) {
        return LocalDateTime.ofEpochSecond(s, 0, ZoneOffset.ofHours(8));
    }

    static public LocalDateTime parseTimestamp(String s) {
        return parseTimestamp(NumberUtils.toLong(s));
    }


    /**
     * 字符串转换成日期
     *
     * @param str     日期字符串
     * @param pattern 日期的格式，如：XDate.DATE_TIME_PATTERN
     */
    public static LocalDateTime parse(String str, DateTimeFormatter pattern) {
        if (XStr.isNullOrEmpty(str)) {
            return null;
        }

        return LocalDateTime.parse(str, pattern);
    }

    public static LocalDateTime parse(String str, String pattern) {
        return parse(str, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 格式：
     * 2022-10-20 12:59:59
     * 2022-10-20
     *
     * @param dateStr
     */
    @SneakyThrows
    static public LocalDateTime parse(String dateStr) {
        String regDateTime = "\\d{4}\\-\\d{2}\\-\\d{2} \\d{2}\\:\\d{2}\\:\\d{2}$";
        String regDate = "\\d{4}\\-\\d{2}\\-\\d{2}$";

        Boolean match = false;
        if (XStr.matches(dateStr, regDateTime)) {
            match = true;
        } else if (XStr.matches(dateStr, regDate)) {
            dateStr += " 00:00:00";
            match = true;
        }

        if (match) {
            //格式正常
            return parse(dateStr, XDate.DATE_TIME_PATTERN_Si);
        }

        throw new Exception("日期格式异常：" + dateStr);
    }

    /**
     * 格式：2022-10-20
     *
     * @param yyyyMMdd
     */
    static public LocalDate parseDate(String yyyyMMdd) {
        return LocalDate.parse(yyyyMMdd, XDate.DATE_PATTERN);
    }


    /**
     * 两个日期之间的天列表
     *
     * @param dateBegin
     * @param dateEnd
     * @return array
     */
    @SneakyThrows
    static public List<String> getStringList(LocalDateTime dateBegin, LocalDateTime dateEnd, Integer type) {
        DateTimeFormatter ofPattern;
        LocalDateTime dataEndNew;
        LocalDateTime dateIndex;
        switch (type) {
            case 0: //日
                ofPattern = DateTimeFormatter.ofPattern("yyyy-MM-dd");
                dataEndNew = parse(format(dateEnd, "yyyy-MM-dd"));
                dateIndex = parse(format(dateBegin, "yyyy-MM-dd"));
                break;
            case 1: //月
                ofPattern = DateTimeFormatter.ofPattern("yyyy-MM");
                dataEndNew = parse(format(dateEnd, "yyyy-MM-01"));
                dateIndex = parse(format(dateBegin, "yyyy-MM-01"));
                break;
            case 2: //年
                ofPattern = DateTimeFormatter.ofPattern("yyyy");
                dataEndNew = parse(format(dateEnd, "yyyy-01-01"));
                dateIndex = parse(format(dateBegin, "yyyy-01-01"));
                break;
            default:
                throw new Exception("type error, " + type);
        }

        List<String> strArr = new LinkedList();
        while (dateIndex.compareTo(dataEndNew) <= 0) {
            strArr.add(dateIndex.format(ofPattern));

            switch (type) {
                case 0: //日
                    dateIndex = dateIndex.plusDays(1);
                    break;
                case 1: //月
                    dateIndex = dateIndex.plusMonths(1);
                    break;
                case 2: //年
                    dateIndex = dateIndex.plusYears(1);
                    break;
                default:
                    throw new Exception("type error, " + type);
            }
        }

        return strArr;
    }

    /**
     * 日期列表
     *
     * @param dateBegin
     * @param dateEnd
     * @param onlyWeek  1-7，【1】周1；【7】周日
     */
    static public List<LocalDateTime> getDateList(LocalDateTime dateBegin, LocalDateTime dateEnd, List<Integer> onlyWeek) {
        if (onlyWeek == null) {
            onlyWeek = List.of(1, 2, 3, 4, 5, 6, 7);
        }
        LocalDateTime dataEndNew = parse(format(dateEnd, "yyyy-MM-dd"));
        LocalDateTime dateIndex = parse(format(dateBegin, "yyyy-MM-dd"));

        List<LocalDateTime> dateArr = new LinkedList();
        while (dateIndex.compareTo(dataEndNew) <= 0) {
            //log.debug("{}", dateIndex.getDayOfWeek().getValue());
            if (!onlyWeek.contains(dateIndex.getDayOfWeek().getValue())) {
                //pass
            } else {
                dateArr.add(dateIndex);
            }

            dateIndex = dateIndex.plusDays(1);
        }

        return dateArr;
    }
}
